﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Data.Services.Client;
using System.Linq;
using System.Linq.Expressions;
using System.Xml.Linq;
using Caliburn.Micro;
using OrbitOne.CRMLyncInfoDashboard.CRM;
using OrbitOne.CRMLyncInfoDashboard.CRM.OData;
using OrbitOne.CRMLyncInfoDashboard.CRM.OrganizationData;
using OrbitOne.CRMLyncInfoDashboard.Lync;

namespace OrbitOne.CRMLyncInfoDashboard.Shell
{
    [Export(typeof(IScreen))]
    public class DetailViewModel : Screen, IDetailViewModel
    {
        private readonly IWindowUtility m_windowUtility;
        private readonly ILyncDashboardFormProvider m_lyncDashboardFormProvider;
        private readonly IActionLinkFactory m_actionLinkFactory;
        public EntityModelTypes EntityModelType { get; set; }
        public IEntityModel EntityModel { get; set; }
        public Endpoint[] Endpoints { get; set; }

        public string EntityTypeName { get { return EntityModel.Metadata.DisplayName; } }
        public string DisplayText { get { return EntityModel.DisplayText; } }
        public string ImageLocation { get { return EntityModel.Metadata.ImageLocation; } }

        public IShellViewModel Conductor { get { return Parent as IShellViewModel; } }

        private DisplayFieldModel[] m_displayFields;

        public DisplayFieldModel[] DisplayFields
        {
            get { return m_displayFields; }
            set
            {
                m_displayFields = value;
                base.NotifyOfPropertyChange(() => DisplayFields);
            }
        }

        [ImportingConstructor]
        public DetailViewModel(IWindowUtility windowUtility,
            ILyncDashboardFormProvider lyncDashboardFormProvider,
            IActionLinkFactory actionLinkFactory)
        {
            m_windowUtility = windowUtility;
            m_lyncDashboardFormProvider = lyncDashboardFormProvider;
            m_actionLinkFactory = actionLinkFactory;
        }

        protected override void OnActivate()
        {
            base.OnActivate();
            RefreshContents();
        }



        private void OnConfigurationLoaded(IAsyncResult ar)
        {
            var query = (ar.AsyncState as DataServiceQuery<SystemForm>);
            if (query == null) return;

            var endExecute = query.EndExecute(ar);
            var dataServiceCollection = new DataServiceCollection<SystemForm>(endExecute);
            var entities = dataServiceCollection.ToList();
            var lyncDashboardFormModel = GetEntityModels<SystemForm, SystemFormModel>(entities).FirstOrDefault();

            if (lyncDashboardFormModel == null) return;

            DisplayEntityCard(lyncDashboardFormModel.FormXml.Element("header"));
        }

        private void DisplayEntityCard(XElement displayFields)
        {

            var cells = displayFields.Descendants("cell").ToList();
            var fieldCollection = cells.Select(cell =>
                                                   {
                                                       var labelsElement = cell.Element("labels");
                                                       if (labelsElement == null)
                                                       {
                                                           throw new ArgumentNullException("The labels element is missing from the form definition.");
                                                       }
                                                       var firstLabel = labelsElement.Elements("label").First();
                                                       var attribute = firstLabel.Attribute("description");
                                                       if (attribute == null)
                                                       {
                                                           throw new ArgumentNullException("The description attribute is missing from the form definition.");
                                                       }
                                                       var labelDescription = attribute.Value;

                                                       var controlElement = cell.Element("control");
                                                       if (controlElement == null)
                                                       {
                                                           throw new ArgumentNullException("The control element is missing from the form definition.");
                                                       }
                                                       var xAttribute = controlElement.Attribute("datafieldname");
                                                       if (xAttribute == null)
                                                       {
                                                           throw new ArgumentNullException("The datafieldname attribute is missing from the form definition.");
                                                       }
                                                       var dataFieldName = xAttribute.Value;
                                                       return new
                                                                  {
                                                                      LabelDescription = labelDescription,
                                                                      DataFieldName = dataFieldName
                                                                  };
                                                   });

            var type = EntityModel.GetType();
            DisplayFields = fieldCollection.Select(df =>
                                                     {
                                                         var propertyInfos = type.GetProperties().Where(p => p.Name.Equals(df.DataFieldName, StringComparison.InvariantCultureIgnoreCase));
                                                         var fieldValue = (propertyInfos.First().GetValue(EntityModel, null) ?? string.Empty).ToString();
                                                         return new DisplayFieldModel(df.LabelDescription, df.DataFieldName, fieldValue);
                                                     }).ToArray();
        }

        private static TModel[] GetEntityModels<T, TModel>(IEnumerable<T> entities)
            where TModel : class
            where T : class
        {
            return entities.Select(e => e == null ? null : (TModel)Activator.CreateInstance(typeof(TModel), e)).ToArray();
        }
        
        public void LogPhoneCallActivity()
        {
            var phoneCallActivity = new PhoneCallModel(EntityModel, EntityModelType) { PhoneNumber = Endpoints.First(e => e.Type == EndpointType.Telephone).Uri };
            m_windowUtility.CreateNewEntityWindow(phoneCallActivity,
                                                  new Expression<Func<PhoneCallModel, object>>[] { pc => pc.PhoneNumber },
                                                  new Dictionary<string, string>
                                                      {
                                                          {"directioncode", ((int)DirectionCode.Incoming).ToString()},
                                                          {"partyid", string.Format("{{{0}}}", EntityModel.Id.ToString())},
                                                          {"partyname", DisplayText},
                                                          {"partytype", ((int)EntityModelType).ToString()}
                                                      });
        }

        public void AddNote()
        {
            var note = new NoteModel(EntityModel, EntityModelType);
            m_windowUtility.PreviewEntityType = EntityModelType;
            m_windowUtility.CreateNewEntityWindow(note,
                                                  new Expression<Func<NoteModel, object>>[] {},
                                                  new Dictionary<string, string>
                                                      {
                                                          {"pId", string.Format("{{{0}}}", EntityModel.Id.ToString())},
                                                          {"pType", ((int) EntityModelType).ToString()}
                                                      });
        }

        public void AddTask()
        {
            var task = new TaskModel();
            m_windowUtility.CreateNewEntityWindow(task,
                                                  new Expression<Func<TaskModel, object>>[]{},
                                                  new Dictionary<string, string>
                                                      {
                                                          {"pId", string.Format("{{{0}}}", EntityModel.Id.ToString())},
                                                          {"pName", DisplayText},
                                                          {"pType", ((int) EntityModelType).ToString()}
                                                      });
        }

        public void EditEntity()
        {
            m_windowUtility.EditEntityWindow(EntityModel);
        }

        public void ShowSummaryView()
        {
            Conductor.ShowSummaryView();
        }

        public void RefreshContents()
        {
            DisplayFields = new DisplayFieldModel[0];
            m_lyncDashboardFormProvider.GetByName(EntityModelType, OnConfigurationLoaded);
        }

        public void OnFieldClick(DisplayFieldModel displayField)
        {
            var actionLink = m_actionLinkFactory.GetActionLink(displayField);
            if (actionLink != null)
            {
                actionLink.Execute();
            }
        }
    }
}